<?php
/**
 * Created by PhpStorm.
 * User: xh
 * Date: 2019/12/16
 * Time: 2:20 PM
 */

namespace weChat\pay;


class Pit
{
    //微信的appId
    public $appId  = null;
    //微信secret
    public $secret = null;
    //微信的mchId
    public $mchId = null;
    //微信key
    public $key = null;
    //加密方式
    public  $signType = 'MD5';

    /** 生成随机字符串
     * @param int $length
     * @param string $chars
     * @return string
     */
    protected function nonceStr(int $length=32, string $chars="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"): string {
        $str = "";
        for ($i = 0; $i < $length; $i++) {
            $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
        }
        return $str;
    }

    /** 生成sign
     * @param array $data
     * @param string $signType
     * @return string
     */
    protected function sign(array $data,string $signType='MD5'): string {
        //签名步骤一：按字典序排序参数
        ksort($data);

        $string = '';
        foreach ($data as $k => $v) {
            if ($k != "sign")
            {$string .= $k . "=" . $v . "&";}
        }
        //签名步骤二：在string后加入KEY
        $string = $string . "key=" . $this->key;

        //签名步骤三：MD5加密或者HMAC-SHA256
        if ($signType == "MD5") {
            $string = md5($string);
        } else if ($signType == "HMAC-SHA256") {
            $string = hash_hmac("sha256", $string, $this->key);
        } else {
            $this->exception('签名类型不支持！');
        }
        //签名步骤四：所有字符转为大写
        return strtoupper($string);
    }

    /** 将xml转为array
     * @param string $xml
     * @param array $data
     * @return bool
     */
    protected function toArray(string $xml,array &$data): bool{
        if( empty($xml) ){
            $this->errorLog($xml,'xml数据不存在！');
            return false;
        }
        //将XML转为array
        //禁止引用外部xml实体
        libxml_disable_entity_loader(true);
        $data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
        return true;
    }

    /** 输出xml字符 array 转换 xml
     * @param array $data
     * @return string
     */
    protected  function toXml(array $data){
        if(!is_array($data) || count($data) <= 0)
        {$this->exception('数组数据异常！');}
        $xml = "<xml>";
        foreach ($data as $key=>$val)
        {
            if (is_numeric($val)){
                $xml.="<".$key.">".$val."</".$key.">";
            }else{
                $xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
            }
        }
        $xml.="</xml>";
        return $xml;
    }

    /** 返回给微信的信息
     * @param string $return_code SUCCESS 正确返回 FAIL错误返回
     * @param string $return_msg ok 正确返回 错误返回 这里自己写
     * @return string
     */
    public function reply(string $return_code='SUCCESS',string $return_msg='OK'){
        //return_code FAIL 错误
        return $this->toXml(array('return_code'=>$return_code,'return_msg'=>$return_msg));
    }

    /** 记录错误
     * @param string $xml
     * @param string $msg
     */
    protected function errorLog(string $xml,string $msg): void{
        $file_dir = './extend/weChat/pay/log/error/';
        create($file_dir);
        $file_name = $file_dir.date('Y-m-d').'.log';
        $file = fopen($file_name,'a');
        fwrite( $file, '-----------------------------'."\n".date('Y-m-d H:i:s').'  '.$msg."\n".$xml."\n".'-----------------------------'."\n\n\n" );
        fclose($file);
    }

    /** 回调xml记录
     * @param string $xml
     * @param string $path
     */
    protected static function xmlLog(string $xml,string $path='callback'): void{
        $file_dir = './extend/weChat/pay/log/'.$path.'/';
        create($file_dir);
        $file_name = $file_dir.date('Y-m-d').'.log';
        $file = fopen($file_name,'a');
        fwrite( $file, '-----------------------------'."\n".date('Y-m-d H:i:s')."\n".$xml."\n".'-----------------------------'."\n\n\n");
        fclose($file);
    }

    /**
     * @param string $error
     */
    protected function exception(string $error){
        try {
            throw new \Exception($error);
        } catch (\Exception $e) {
            echo json_encode(array('code'=>70000,'msg'=>$e->getMessage(),'data'=>array()));
            die();
        }
    }

    /** curl请求
     * @param string $returnXml
     * @param string $xml
     * @param string $url
     * @param bool $useCert
     * @param string $path
     * @param int $second
     */
    protected function curl(string &$returnXml, string $xml, string $url, bool $useCert = false,string $path='cert', int $second = 30): void
    {
        $ch = curl_init();
        $curlVersion = curl_version();
        $ua = "WXPaySDK/3.0.9 (" . PHP_OS . ") PHP/" . PHP_VERSION . " CURL/" . $curlVersion['version'] . " "
            . $this->appId;
        //设置超时
        curl_setopt($ch, CURLOPT_TIMEOUT, $second);
        // $proxyHost = "0.0.0.0";
        // $proxyPort = 0;
        // //如果有配置代理这里就设置代理
        // if($proxyHost != "0.0.0.0" && $proxyPort != 0){
        //     curl_setopt($ch,CURLOPT_PROXY, $proxyHost);
        //     curl_setopt($ch,CURLOPT_PROXYPORT, $proxyPort);
        // }
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, TRUE);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);//严格校验
        curl_setopt($ch, CURLOPT_USERAGENT, $ua);
        //设置header
        curl_setopt($ch, CURLOPT_HEADER, FALSE);
        //要求结果为字符串且输出到屏幕上
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);

        if ($useCert == true) {
            //设置证书
            //使用证书：cert 与 key 分别属于两个.pem文件
            //证书文件请放入服务器的非web目录下
            $sslCertPath = __DIR__ . '/'.$path.'/apiclient_cert.pem';
            $sslKeyPath = __DIR__ . '/'.$path.'/apiclient_key.pem';
            curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM');
            curl_setopt($ch, CURLOPT_SSLCERT, $sslCertPath);
            curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');
            curl_setopt($ch, CURLOPT_SSLKEY, $sslKeyPath);
        }
        //post提交方式
        curl_setopt($ch, CURLOPT_POST, TRUE);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
        //运行curl
        $returnXml = curl_exec($ch);
        //返回结果
        if ($returnXml) {
            curl_close($ch);
        } else {
            $error = curl_errno($ch);
            curl_close($ch);
            $this->exception('curl出错，错误码:' . $error);
        }
    }

    /**
     * json请求
     * @param string $url
     * @param array $param
     * @param string $contentType
     */
    public function toJson($url = '', $param = [] ,$contentType = 'json'){
        $ch = curl_init();
        // 请求地址
        curl_setopt($ch, CURLOPT_URL, $url);
        // 请求参数类型
        $param = $contentType == 'json' ? urldecode(json_encode($param)) : http_build_query($param);
        // 关闭https验证
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        // post提交
        if($param){
            curl_setopt($ch, CURLOPT_POST, 1);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $param);
        }
        // 返回的数据是否自动显示
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        // 执行并接收响应结果
        $output = curl_exec($ch);
        // 关闭curl
        curl_close($ch);
        return $output !== false ? $output : false;

    }
}